<template>
  <div>
    <div class="mindMapContainer" ref="mindMapContainerRef"></div>

    <!-- 右键菜单 -->
    <div
      v-if="showContextMenu"
      class="context-menu"
      :style="{ top: `${menuPosition.y}px`, left: `${menuPosition.x}px` }"
    >
      <ul>
        <li @click="addChildNode">添加子节点</li>
        <li @click="addSameNode">添加同级节点</li>
        <li @click="removeNode">删除节点</li>
        <li @click="copyNode">复制节点</li>
        <li @click="pasteNode">粘贴节点</li>
      </ul>
    </div>
    <div class="active-panel" v-show="!props.readonly && activedPanel !== ''">
      <!-- 图标 -->
      <div class="active-panel-box" v-if="activedPanel === 'icon'">
        <div class="title">
          选择图标
          <span class="close" @click="activedPanel = ''">×</span>
        </div>
        <div class="content icon">
          <div
            class="icon-group"
            v-for="group of nodeIconList"
            :key="group.type"
          >
            <div class="group-title">
              {{ group.name }}
            </div>
            <div class="group-content">
              <span
                class="icon-item"
                :class="activeNodes.length === 0 ? 'disabled' : ''"
                v-for="icon of group.list"
                :key="icon.name"
                @click="setIcon(group.type, icon.name)"
                v-html="icon.icon"
              ></span>
            </div>
          </div>
        </div>
      </div>

      <!-- 主题 -->
      <div class="active-panel-box" v-else-if="activedPanel === 'theme'">
        <div class="title">
          选择主题
          <span class="close" @click="activedPanel = ''">×</span>
        </div>
        <div class="content">
          <li
            v-for="item of themes"
            :key="item.value"
            @click="changeTheme(item.value)"
          >
            <img :src="`/src/assets/images/themes/${item.value}.jpg`" />
            <div>{{ item.label }}</div>
          </li>
        </div>
      </div>

      <!-- 结构 -->
      <div class="active-panel-box" v-else-if="activedPanel === 'layout'">
        <div class="title">
          选择结构
          <span class="close" @click="activedPanel = ''">×</span>
        </div>
        <div class="content">
          <li
            v-for="item of layouts"
            :key="item.value"
            @click="changeLayout(item.value)"
          >
            <img :src="`/src/assets/images/layout/${item.value}.png`" />
            <div>{{ item.label }}</div>
          </li>
        </div>
      </div>

      <!-- 标签 -->
      <div class="active-panel-box" v-else-if="activedPanel === 'tag'">
        <div class="title">
          选择标签
          <span class="close" @click="activedPanel = ''">×</span>
        </div>
        <div class="content">
          <y-tag v-model="tagModel" :closable="true" />
        </div>
      </div>

      <!-- 备注 -->
      <div class="active-panel-box" v-else-if="activedPanel === 'note'">
        <div class="title">
          添加备注
          <span class="close" @click="activedPanel = ''">×</span>
        </div>
        <div class="content">
          <textarea
            style="
              min-height: 31px;
              height: 250px;
              resize: vertical;
              border: ridge;
            "
            class="el-textarea"
            v-model="noteModel"
            placeholder="请输入内容"
          />
        </div>
      </div>
    </div>
  </div>
</template>

<script setup>
const { proxy } = getCurrentInstance();

import MindMap from "simple-mind-map";
import MiniMap from "simple-mind-map/src/plugins/MiniMap.js";
import Watermark from "simple-mind-map/src/plugins/Watermark.js";
import KeyboardNavigation from "simple-mind-map/src/plugins/KeyboardNavigation.js";
import ExportPDF from "simple-mind-map/src/plugins/ExportPDF.js";
import ExportXMind from "simple-mind-map/src/plugins/ExportXMind.js";
import Export from "simple-mind-map/src/plugins/Export.js";
import Drag from "simple-mind-map/src/plugins/Drag.js";
import Select from "simple-mind-map/src/plugins/Select.js";
import AssociativeLine from "simple-mind-map/src/plugins/AssociativeLine.js";
import TouchEvent from "simple-mind-map/src/plugins/TouchEvent.js";
import NodeImgAdjust from "simple-mind-map/src/plugins/NodeImgAdjust.js";
import Search from "simple-mind-map/src/plugins/Search.js";
import Painter from "simple-mind-map/src/plugins/Painter.js";
import Scrollbar from "simple-mind-map/src/plugins/Scrollbar.js";
import Formula from "simple-mind-map/src/plugins/Formula.js";
import Cooperate from "simple-mind-map/src/plugins/Cooperate.js";
import { nodeIconList } from "simple-mind-map/src/svg/icons";
import xmind from "simple-mind-map/src/parse/xmind.js";

// 注册插件
MindMap.usePlugin(MiniMap)
  .usePlugin(Watermark)
  .usePlugin(Drag)
  .usePlugin(KeyboardNavigation)
  .usePlugin(ExportPDF)
  .usePlugin(ExportXMind)
  .usePlugin(Export)
  .usePlugin(Select)
  .usePlugin(AssociativeLine)
  .usePlugin(NodeImgAdjust)
  .usePlugin(TouchEvent)
  .usePlugin(Search)
  .usePlugin(Painter)
  .usePlugin(Scrollbar)
  .usePlugin(Formula)
  .usePlugin(Cooperate);

// 主题
import cactus from "./themes/cactus";
import classic5 from "./themes/classic5";
import classic6 from "./themes/classic6";
import classic7 from "./themes/classic7";
import dark3 from "./themes/dark3";
import dark4 from "./themes/dark4";
import darkNightLceBlade from "./themes/darkNightLceBlade";
import index from "./themes/index";
import lemonBubbles from "./themes/lemonBubbles";
import morandi from "./themes/morandi";
import neonLamp from "./themes/neonLamp";
import oreo from "./themes/oreo";
import rose from "./themes/rose";
import seaBlueLine from "./themes/seaBlueLine";
import shallowSea from "./themes/shallowSea";

MindMap.defineTheme("cactus", cactus);
MindMap.defineTheme("classic5", classic5);
MindMap.defineTheme("classic6", classic6);
MindMap.defineTheme("classic7", classic7);
MindMap.defineTheme("dark3", dark3);
MindMap.defineTheme("dark4", dark4);
MindMap.defineTheme("darkNightLceBlade", darkNightLceBlade);
MindMap.defineTheme("index", index);
MindMap.defineTheme("lemonBubbles", lemonBubbles);
MindMap.defineTheme("morandi", morandi);
MindMap.defineTheme("neonLamp", neonLamp);
MindMap.defineTheme("oreo", oreo);
MindMap.defineTheme("rose", rose);
MindMap.defineTheme("seaBlueLine", seaBlueLine);
MindMap.defineTheme("shallowSea", shallowSea);

let mindMap = null;
const mindMapContainerRef = ref();

const props = defineProps({
  mindData: { type: Object, default: {} },
  name: { type: String, default: "思维导图" },
  readonly: {
    type: Boolean,
    default: false,
  },
});

const defaultData = ref({
  data: {
    text: "根节点",
  },
  children: [
    {
      data: {
        text: "二级节点",
        image: "",
        imageTitle: "",
        hyperlink: "",
        note: "",
      },
      children: [],
    },
    {
      data: {
        text: "二级节点",
        image: "",
        imageTitle: "",
        hyperlink: "",
        note: "",
      },
      children: [],
    },
  ],
});

// 当前节点
const currentNode = shallowRef(null);
// 当前激活节点
const activeNodes = shallowRef([]);
// 当前节点图标列表
const currentIconList = ref([]);
// 当前激活右侧工具栏
const activedPanel = ref("");
// 是否显示菜单
const showContextMenu = ref(false);
// 菜单显示的位置
const menuPosition = ref({ x: 0, y: 0 });
// 支持的组织结构

const tagModel = computed({
  get() {
    if (activeNodes.value.length > 0) {
      let firstNode = activeNodes.value[0];
      return firstNode.getData("tag") || [];
    }
    return [];
  },
  set(value) {
    activeNodes.value.forEach((node) => {
      node.setTag(value);
    });
  },
});

const noteModel = computed({
  get() {
    if (activeNodes.value.length > 0) {
      let firstNode = activeNodes.value[0];
      return firstNode.getData("note") || "";
    }
    return "";
  },
  set(value) {
    activeNodes.value.forEach((node) => {
      node.setNote(value);
    });
  },
});

const currentTheme = ref("default");
const themes = ref([
  { value: "default", label: "默认" },
  { value: "classic", label: "经典" },
  { value: "classic2", label: "经典2" },
  { value: "classic3", label: "经典3" },
  { value: "classic4", label: "经典4" },
  { value: "classic5", label: "经典5" },
  { value: "classic6", label: "经典6" },
  { value: "classic7", label: "经典7" },
  { value: "simpleBlack", label: "简约黑" },
  { value: "dark", label: "暗色" },
  { value: "dark2", label: "暗色2" },
  { value: "dark3", label: "暗色3" },
  { value: "dark4", label: "暗色4" },
  { value: "darkNightLceBlade", label: "暗夜兵刃" },
  { value: "neonLamp", label: "霓虹灯" },
  { value: "orangeJuice", label: "橙汁" },
  { value: "blackGold", label: "黑金" },
  { value: "lateNightOffice", label: "深夜办公室" },
  { value: "blackHumour", label: "黑色幽默" },
  { value: "mint", label: "薄荷" },
  { value: "pinkGrape", label: "粉红葡萄" },
  { value: "romanticPurple", label: "浪漫紫" },
  { value: "earthYellow", label: "泥土黄" },
  { value: "freshGreen", label: "清新绿" },
  { value: "freshRed", label: "清新红" },
  { value: "brainImpairedPink", label: "脑残粉" },
  { value: "blueSky", label: "天空蓝" },
  { value: "classicBlue", label: "经典蓝" },
  { value: "classicGreen", label: "经典绿" },
  { value: "skyGreen", label: "天清绿" },
  { value: "vitalityOrange", label: "活力橙" },
  { value: "autumn", label: "秋天" },
  { value: "avocado", label: "牛油果" },
  { value: "cactus", label: "仙人掌" },
  { value: "lemonBubbles", label: "柠檬气泡" },
  { value: "coffee", label: "咖啡" },
  { value: "courseGreen", label: "课程绿" },
  { value: "gold", label: "金色vip" },
  { value: "greenLeaf", label: "绿叶" },
  { value: "minions", label: "小黄人" },
  { value: "morandi", label: "莫兰迪" },
  { value: "redSpirit", label: "红色精神" },
  { value: "oreo", label: "奥利奥" },
  { value: "rose", label: "玫瑰" },
  { value: "seaBlueLine", label: "海蓝线" },
  { value: "shallowSea", label: "浅海" },
]);
const currentLayout = ref("mindMap");
const layouts = ref([
  { value: "mindMap", label: "思维导图" },
  { value: "logicalStructure", label: "逻辑结构图" },
  { value: "organizationStructure", label: "组织结构图" },
  { value: "catalogOrganization", label: "目录组织图" },
  { value: "timeline", label: "时间轴" },
  { value: "timeline2", label: "时间轴2" },
  { value: "fishbone", label: "鱼骨图" },
  { value: "verticalTimeline", label: "垂直时间轴" },
]);

function init() {
  if (mindMap) {
    mindMap.destroy();
  }
  let data = defaultData.value;
  if (props.mindData.root) {
    data = props.mindData.root;
    currentLayout.value = props.mindData.layout;
    currentTheme.value = props.mindData.theme.template;
  }
  mindMap = new MindMap({
    el: mindMapContainerRef.value,
    data: data,
    theme: currentTheme.value,
    layout: currentLayout.value,
    readonly: props.readonly,
    nodeTextEditZIndex: 1000,
    nodeNoteTooltipZIndex: 1000,
    initRootNodePosition: ["center", "center"],
  });

  // 节点激活事件
  mindMap.on("node_active", (node, activeNodeList) => {
    activeNodes.value = activeNodeList;
  });

  // 节点右键事件
  mindMap.on("node_contextmenu", (e, node) => {
    if (e.which == 3) {
      menuPosition.value = { x: e.clientX + 10, y: e.clientY + 10 };
      showContextMenu.value = true;
      currentNode.value = node;
    }
  });

  // 点击空白处
  mindMap.on("node_click", hide);
  mindMap.on("draw_click", hide);
  mindMap.on("expand_btn_click", hide);
}

/** 监听 props.mindData 变化并初始化 */
watch(
  () => props.mindData,
  (newVal, oldVal) => {
    if (newVal !== oldVal) {
      init();
    }
  },
  { deep: true }
);

// 激活节点是否含有根节点
const hasRoot = computed(() => {
  return (
    activeNodes.value.findIndex((node) => {
      return node.isRoot;
    }) !== -1
  );
});

// 激活节点是否含有概要节点
const hasGeneralization = computed(() => {
  return (
    activeNodes.value.findIndex((node) => {
      return node.isGeneralization;
    }) !== -1
  );
});

/** 隐藏右侧菜单 */
const hide = () => {
  menuPosition.value = { x: 0, y: 0 };
  showContextMenu.value = false;
  currentNode.value = null;
};

/** 添加节点 */
const addChildNode = () => {
  if (mindMap) {
    mindMap.execCommand("INSERT_CHILD_NODE");
  }
  showContextMenu.value = false; // 关闭菜单
};

/** 添加同级节点 */
const addSameNode = () => {
  if (mindMap) {
    mindMap.execCommand("INSERT_NODE");
  }
  showContextMenu.value = false; // 关闭菜单
};

/** 删除节点 */
const removeNode = () => {
  if (mindMap && currentNode.value) {
    mindMap.execCommand("REMOVE_NODE");
  }
  showContextMenu.value = false; // 关闭菜单
};

/** 复制节点 */
const copyNode = () => {
  if (mindMap && currentNode.value) {
    mindMap.renderer.copy();
  }
  showContextMenu.value = false; // 关闭菜单
};

/** 粘贴节点 */
const pasteNode = () => {
  if (mindMap && currentNode.value) {
    mindMap.renderer.paste();
  }
  showContextMenu.value = false; // 关闭菜单
};

/** 导出功能 */
const onExport = (type) => {
  if (mindMap) {
    mindMap.export(type, true, props.name);
  }
};

/** 查询数据 */
const getMindData = () => {
  if (mindMap) {
    return mindMap.getData(true);
  }
};

const setIcon = (type, name) => {
  if (activeNodes.value.length === 0) return;
  let key = type + "_" + name;
  // 检查当前节点是否存在该图标
  let index = currentIconList.value.findIndex((item) => {
    return item === key;
  });
  // 存在则删除icon
  if (index !== -1) {
    currentIconList.value.splice(index, 1);
  } else {
    // 否则判断当前图标是否和要插入的图标是一个组的
    let typeIndex = currentIconList.value.findIndex((item) => {
      return item.split("_")[0] === type;
    });
    // 是一个组的则进行替换
    if (typeIndex !== -1) {
      currentIconList.value.splice(typeIndex, 1, key);
    } else {
      // 否则添加icon
      currentIconList.value.push(key);
    }
  }
  activeNodes.value.forEach((node) => {
    node.setIcon([...currentIconList.value]);
  });
};

const changeLayout = (layout) => {
  currentLayout.value = layout;
  mindMap.setLayout(layout);
};

const changeTheme = (theme) => {
  currentTheme.value = theme;
  mindMap.setTheme(theme);
};

// 导入文件
const onInput = (e) => {
  let file = e.target.files[0];
  let reg = /\.(smm|xmind|json|md)$/;
  if (!reg.test(file.name)) {
    proxy.$modal.msgWarning("请选择.smm、.json、.xmind、.md文件！");
    return;
  }
  if (/\.(smm|json)$/.test(file.name)) {
    handleSmm(file);
  } else if (/\.xmind$/.test(file.name)) {
    handleXmind(file);
  } else if (/\.xlsx$/.test(file.name)) {
    handleExcel(file);
  } else if (/\.md$/.test(file.name)) {
    handleMd(file);
  }
  proxy.$modal.msgSuccess("导入成功！");
};

// 处理.smm、.json文件
const handleSmm = (file) => {
  let fileReader = new FileReader();
  fileReader.readAsText(file);
  fileReader.onload = (evt) => {
    try {
      let data = JSON.parse(evt.target.result);
      if (typeof data !== "object") {
        throw new Error("文件内容有误");
      }
      setData(data);
    } catch (error) {
      console.log(error);
    }
  };
};

// 处理.xmind文件
const handleXmind = async (file) => {
  try {
    let data = await xmind.parseXmindFile(file);
    setData(data);
  } catch (error) {
    console.log(error);
  }
};

// 处理.xlsx文件
const handleExcel = async (file) => {
  // 略
};

// 处理markdown文件
const handleMd = async (file) => {
  let fileReader = new FileReader();
  fileReader.readAsText(file);
  fileReader.onload = async (evt) => {
    try {
      let data = await MindMap.markdown.transformMarkdownTo(evt.target.result);
      setData(data);
    } catch (error) {
      console.log(error);
    }
  };
};

// 导入数据
const setData = (data) => {
  if (data.root) {
    mindMap.setFullData(data);
  } else {
    mindMap.setData(data);
  }
  mindMap.view.reset();
};

defineExpose({
  activeNodes,
  activedPanel,
  getMindData,
  onInput,
  onExport,
});
</script>
<style lang="scss" scoped>
.mindMapContainer {
  margin: 0;
  padding: 0;
  width: 100%;
  height: calc(100vh - 190px);
}
.top-menu-fixed {
  position: fixed;
  top: 100px;
  left: 50%;
  width: 180px;
  z-index: 1000;
  display: flex;
  justify-content: space-around;
  background-color: #fff;
  padding: 10px 20px;
  border-radius: 6px;
  box-shadow: 0 2px 16px 0 rgba(0, 0, 0, 0.06);
  border: 1px solid rgba(0, 0, 0, 0.06);
  margin-right: 20px;
  .top-menu-item {
    width: 50px;
    text-align: center;
    border: 1px solid rgba(0, 0, 0, 0.06);
    cursor: pointer;
    padding: 3px 0px;
    border-radius: 5px;
    .top-menu-item--text {
      font-size: 14px;
    }
  }
}

.context-menu {
  position: fixed;
  background-color: white;
  border: 1px solid #ccc;
  box-shadow: 0px 2px 10px rgba(0, 0, 0, 0.1);
  z-index: 1000;
  padding: 10px;
  border-radius: 4px;
}

.context-menu ul {
  list-style: none;
  padding: 0;
  margin: 0;
}

.context-menu li {
  padding: 8px 12px;
  cursor: pointer;
}

.context-menu li:hover {
  background-color: #f0f0f0;
}

$iconSize: 1.4rem;

.active-panel {
  position: absolute;
  top: 10rem;
  right: 5px;
  bottom: 10rem;
  width: 16rem;
  background: white;
  user-select: none;
  .active-panel-box {
    height: 100%;
    .title {
      height: 3rem;
      line-height: 3rem;
      padding: 0 0.8rem;
      font-weight: bold;
      font-size: 1.1rem;
      border-bottom: 1px solid #ccc;
      .close {
        float: right;
        cursor: pointer;
        font-size: 1.3rem;
      }
    }
    .content {
      height: calc(100% - 3rem);
      overflow-y: auto;
      overflow-x: hidden;
      padding: 1rem;
      &.icon {
        padding-right: 0.5rem;
      }

      .icon-group {
        margin-top: 1rem;
        line-height: 2;
        &:first-child {
          margin-top: 0;
        }
      }
      .group-title {
        font-weight: bold;
      }
      .group-content {
        display: flex;
        flex-direction: row;
        flex-wrap: wrap;
        .icon-item {
          width: $iconSize;
          height: $iconSize;
          margin-right: 0.4rem;
          margin-top: 0.4rem;
          cursor: pointer;
          &.disabled {
            cursor: not-allowed;
            opacity: 0.5;
          }
          svg {
            width: $iconSize;
            height: $iconSize;
          }
        }
      }

      li {
        list-style: none;
        text-align: center;
        margin-top: 1rem;
        border: 1px solid #ccc;
        border-radius: 4px;
        &:first-child {
          margin-top: 0;
        }
        &:hover {
          cursor: pointer;
          box-shadow: 4px 4px 8px rgba(0, 0, 0, 0.2);
        }
        &.active {
          border: 1px solid #67c23a;
          box-shadow: 4px 4px 8px rgba(0, 0, 0, 0.2);
        }
        img {
          width: 100%;
          height: 8rem;
        }
      }
    }
  }
}
</style>
